﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace System
{
    /// <summary>
    /// TypeExtension
    /// </summary>
    public static class TypeExtension
    {
        public static IEnumerable<T> GetInstancesOfTypesImplementing<T>(this AppDomain appDomain)
        {
            return appDomain.GetAssemblies().GetInstancesOfTypesImplementing<T>();
        }

        public static IEnumerable<T> GetInstancesOfTypesImplementing<T>(this ICollection<Assembly> assemblies)
        {
            var types = assemblies.GetTypesImplementing<T>();
            return types.Select(type => (T) Activator.CreateInstance(type));
        }

        /// <summary>
        /// 类型搜索
        /// </summary>
        /// <param name="appDomin"></param>
        /// <param name="assignTypeFrom"></param>
        /// <param name="onlyConcreteClasses"></param>
        /// <returns></returns>
        public static IEnumerable<Type> FindClassesOfType(this AppDomain appDomin, Type assignTypeFrom, bool onlyConcreteClasses = true)
        {
            return FindClassesOfType(assignTypeFrom, appDomin.GetAssemblies(), onlyConcreteClasses);
        }
        public static IEnumerable<Type> GetTypesImplementing<T>(this ICollection<Assembly> assemblies)
        {
            var result = new List<Type>();
            foreach (var assembly in assemblies)
            {
                result.AddRange(assembly.GetTypesImplementing<T>());
            }
            return result;
        }

        /// <summary>
        /// 获取此程序集中定义的公共类型，这些公共类型在程序集外可见
        /// </summary>
        public static IEnumerable<Type> GetExportedTypes(this ICollection<Assembly> assemblies)
        {
            var result = new List<Type>();
            foreach (var assembly in assemblies)
            {
                result.AddRange(assembly.GetExportedTypes());

            }
            return result;
        }

        //public static void ManualLoad(this AppDomain appDomain,string directoryPath)
        //{
        //    if (!Directory.Exists(directoryPath))
        //    {
        //        return;
        //    }
        //    var loadedAssemblyNames = appDomain.GetAssemblies().Select(a => a.FullName).ToList();
        //    foreach (string dllPath in Directory.GetFiles(directoryPath, "*.dll"))
        //    {
        //        try
        //        {
        //            var assemblyName = AssemblyName.GetAssemblyName(dllPath);
        //            if (!loadedAssemblyNames.Contains(assemblyName.FullName))
        //            {
        //                Assembly.ReflectionOnlyLoad("")
                        
        //            }
                   
        //            //old loading stuff
        //            //Assembly a = Assembly.ReflectionOnlyLoadFrom(dllPath);
        //            //if (Matches(a.FullName) && !loadedAssemblyNames.Contains(a.FullName))
        //            //{
        //            //    App.Load(a.FullName);
        //            //}
        //        }
        //        catch (BadImageFormatException ex)
        //        {
        //            Trace.TraceError(ex.ToString());
        //        }
        //    }
        //    appDomain.Load("")
        //}

        public static IEnumerable<Type> GetTypes(this AppDomain appDomain, Func<Type, bool> predicate)
        {
            return appDomain.GetAssemblies().GetTypes(predicate);
        }

        public static IEnumerable<Type> GetTypes(this ICollection<Assembly> assemblies, Func<Type, bool> predicate)
        {
            return assemblies.SelectMany(assembly => assembly.GetTypes().Where(predicate));
        }

        public static IEnumerable<Type> GetTypesImplementing<T>(this Assembly assembly)
        {
            return assembly.GetExportedTypes().Where(t => t.IsPublic &&
                                                          !t.IsAbstract &&
                                                          typeof(T).IsAssignableFrom(t));
        }
        /// <summary>
        /// 类型搜索
        /// </summary>
        /// <param name="assignTypeFrom"></param>
        /// <param name="assemblies"></param>
        /// <param name="onlyConcreteClasses"></param>
        /// <returns></returns>
        public static IEnumerable<Type> FindClassesOfType(this Type assignTypeFrom, IEnumerable<Assembly> assemblies, bool onlyConcreteClasses = true)
        {
            var result = new List<Type>();
            try
            {
                foreach (var a in assemblies)
                {
                    foreach (var t in a.GetTypes())
                    {
                        if (assignTypeFrom.IsAssignableFrom(t) || (assignTypeFrom.IsGenericTypeDefinition && DoesTypeImplementOpenGeneric(t, assignTypeFrom)))
                        {
                            if (!t.IsInterface)
                            {
                                if (onlyConcreteClasses)
                                {
                                    if (t.IsClass && !t.IsAbstract)
                                    {
                                        result.Add(t);
                                    }
                                }
                                else
                                {
                                    result.Add(t);
                                }
                            }
                        }
                    }

                }
            }
            catch (ReflectionTypeLoadException ex)
            {
                var msg = string.Empty;
                foreach (var e in ex.LoaderExceptions)
                    msg += e.Message + Environment.NewLine;

                var fail = new Exception(msg, ex);
                Debug.WriteLine(fail.Message, fail);
                throw fail;
            }
            return result;
        }
        /// <summary>
        /// Does type implement generic?
        /// </summary>
        /// <param name="type"></param>
        /// <param name="openGeneric"></param>
        /// <returns></returns>
        public static bool DoesTypeImplementOpenGeneric(Type type, Type openGeneric)
        {
            try
            {
                var genericTypeDefinition = openGeneric.GetGenericTypeDefinition();
                foreach (var implementedInterface in type.FindInterfaces((objType, objCriteria) => true, null))
                {
                    if (!implementedInterface.IsGenericType)
                        continue;

                    var isMatch = genericTypeDefinition.IsAssignableFrom(implementedInterface.GetGenericTypeDefinition());
                    return isMatch;
                }
                return false;
            }
            catch
            {
                return false;
            }
        }
        /// <summary>
        /// 检索应用于指定成员的指定类型的自定义特性
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>

        public static T GetFirstCustomAttribute<T>() where T : Attribute
        {
            return typeof (T).GetCustomAttributes<T>(true).FirstOrDefault();
        }
    }
}
